safe-outputs: make create-pull-request bundling resilient to diverged-history replay conflicts#40720
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
✅ PR Code Quality Reviewer completed the code quality review. |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
|
✅ Test Quality Sentinel completed test quality analysis. |
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR #40720 does not have the 'implementation' label and has 0 new lines of code in business logic directories (≤100 threshold). |
There was a problem hiding this comment.
Pull request overview
This PR improves the safe-outputs create_pull_request fallback-to-issue path by embedding clearer diagnostics in the generated fallback issue body when a git push fails, making manual recovery more deterministic.
Changes:
- Include an
**Original error:** …line in push-failure fallback issue bodies (bundle + patch fallback paths). - Update the JS unit test to assert the new fallback issue content.
- Minor formatting change in the generated agentic maintenance workflow header comments.
Show a summary per file
| File | Description |
|---|---|
| actions/setup/js/create_pull_request.cjs | Adds the original push error message into fallback issue bodies for push-failure scenarios. |
| actions/setup/js/create_pull_request.test.cjs | Extends fallback issue assertions to validate the new **Original error:** … content. |
| .github/workflows/agentics-maintenance.yml | Adjusts whitespace in generated header comment separators. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/3 changed files
- Comments generated: 6
| const pushFailureMessage = neutralizeClosingKeywordsForIssueBody(pushError instanceof Error ? pushError.message : String(pushError)) | ||
| .replace(/\s+/g, " ") | ||
| .trim(); |
| const pushFailureMessage = neutralizeClosingKeywordsForIssueBody(pushError instanceof Error ? pushError.message : String(pushError)) | ||
| .replace(/\s+/g, " ") | ||
| .trim(); |
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /tdd, /diagnose, and /improve-codebase-architecture — requesting changes on test coverage gaps.
📋 Key Themes & Highlights
Key Issues
- Missing symmetric test: The patch fallback path got the same
pushFailureMessageaddition as the bundle path, but only the bundle path has a test assertion for it. A regression on the patch path would go undetected. - Keyword-neutralization untested on error text:
neutralizeClosingKeywordsForIssueBodyis called on the raw push error message — the correctness of this on error strings that happen to contain closing keywords (e.g. echoed commit messages during rebase failures) is not covered by any test. - Duplicated construction: The 3-line
pushFailureMessagepattern is copy-pasted between the bundle and patch paths; extracting aformatPushErrorForIssueBody(err)helper would keep the two paths in sync and make the helper independently testable. - Noisy YAML diff: The
agentics-maintenance.ymlchanges are purely trailing whitespace on comment lines — no functional change, worth reverting to keep the diff focused.
Positive Highlights
- ✅ Core fix is correct: embedding the push error in the fallback issue body is exactly the right signal for manual recovery.
- ✅ Whitespace normalization (
.replace(/\s+/g, " ").trim()) correctly collapses multi-line conflict output into a single readable line. - ✅ Wrapping in
neutralizeClosingKeywordsForIssueBodyshows good defensive instinct — push errors can echo commit messages containing closing keywords. - ✅ The existing test is well-structured and verifies the happy path end-to-end.
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 53.2 AIC · ⌖ 9.81 AIC · ⊞ 6.5K
| const fallbackBundleTempRef = createBundleTempRef(branchName); | ||
| const pushFailureMessage = neutralizeClosingKeywordsForIssueBody(pushError instanceof Error ? pushError.message : String(pushError)) | ||
| .replace(/\s+/g, " ") | ||
| .trim(); |
There was a problem hiding this comment.
[/improve-codebase-architecture] The pushFailureMessage construction is copy-pasted verbatim in two fallback paths (bundle ~L1628 and patch ~L1974). A small helper would make future changes to sanitization or normalization consistent.
💡 Suggested extraction
/**
* Formats a push error for safe embedding in a GitHub issue body.
* Neutralizes auto-closing keywords and collapses whitespace.
*/
function formatPushErrorForIssueBody(err) {
return neutralizeClosingKeywordsForIssueBody(
err instanceof Error ? err.message : String(err)
)
.replace(/\s+/g, " ")
.trim();
}Then both call sites become a single readable line:
const pushFailureMessage = formatPushErrorForIssueBody(pushError);This also makes the function independently testable.
| # For more information: https://github.github.com/gh-aw/introduction/overview/ | ||
| # | ||
| # This file defines the generated agentic maintenance workflow for this repository. | ||
| # It runs scheduled cleanup for expiring safe outputs and supports manual maintenance operations. |
There was a problem hiding this comment.
[/diagnose] These three changes swap # comment lines for # (with trailing space). This adds trailing whitespace noise to the diff without any functional effect — and may cause downstream linters or editors to flag it.
Consider reverting these lines to keep the diff minimal and focused on the actual fix.
🧪 Test Quality Sentinel Report✅ Test Quality Score: 100/100 — Excellent
📊 Metrics & Test Classification (1 test analyzed)
Go: 0 ( Verdict
References: §27926298579
|
There was a problem hiding this comment.
Two non-blocking findings; no correctness or crash bugs found in the changed lines.
### Findings summary
1. Missing test for patch fallback path (medium) — neutralizeClosingKeywordsForIssueBody + error embedding was added to both the bundle and patch fallback paths, but only the bundle path got a test assertion. The patch path is a silent blind spot.
2. Generated file trailing-whitespace churn (low) — agentics-maintenance.yml is marked DO NOT EDIT / regenerated by maintenance_workflow.go. The only change is # → # on blank comment lines, which is either a direct-edit violation or a generator regression.
The core logic (sanitizing push error messages with neutralizeClosingKeywordsForIssueBody before embedding in fallback issues) is correct and the bundle path test is adequate.
🔎 Code quality review by PR Code Quality Reviewer · 89.7 AIC · ⌖ 7.47 AIC · ⊞ 5.1K
| } | ||
|
|
||
| const patchFileName = patchFilePath ? patchFilePath.replace("/tmp/gh-aw/", "") : "aw-unknown.patch"; | ||
| const pushFailureMessage = neutralizeClosingKeywordsForIssueBody(pushError instanceof Error ? pushError.message : String(pushError)) |
There was a problem hiding this comment.
Patch fallback path has no test for the new error-message behavior: the second pushFailureMessage block was added without a corresponding assertion, so a regression in this path ships silently.
💡 Details
The bundle fallback path (~line 1628) gained a test assertion:
expect(fallbackIssueBody).toContain("**Original error:** push rejected");But this patch fallback path got the identical logic with zero coverage. If the sanitization logic silently diverges or the template literal interpolation is accidentally dropped here, nothing catches it.
Add a parallel test that exercises the patch fallback path (using patchFilePath with a mocked pushSignedSpy rejection) and asserts **Original error:** appears in the resulting issue body.
| # This file defines the generated agentic maintenance workflow for this repository. | ||
| # It runs scheduled cleanup for expiring safe outputs and supports manual maintenance operations. | ||
| # | ||
| # |
There was a problem hiding this comment.
Generated file directly modified with trailing-whitespace churn: this file is marked DO NOT EDIT and regenerated by maintenance_workflow.go. The only change is replacing # with # (trailing space) on three blank comment lines.
💡 Details
The file header says:
# This file was automatically generated by pkg/workflow/maintenance_workflow.go. DO NOT EDIT.
# To regenerate this workflow, run:
# gh aw compile
If this was a direct edit, it violates the repo convention and will be overwritten the next time gh aw compile runs. If it was produced by running gh aw compile, the generator now emits trailing whitespace — a regression that will trip whitespace linters and produce noisy diffs on every subsequent compile.
Either revert this file to its previous state and fix the generator, or confirm it was regenerated intentionally and suppress the trailing-whitespace lint rule for this generated file.
|
@copilot Run pr-finisher skill |
…, add patch fallback test Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done. Addressed all review threads in commit
All local validation passed ( |
|
@copilot review all comments and address the open patch-path review thread.
|
|
@copilot add a git integration test to cover this scenario, and include detailed comments and logging explaining the reason for this change. |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…omments Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…ments Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Added a new git integration test in The test:
Detailed inline comments explain every step and tie each assertion back to the original failure mode. Committed in |
|
`` Please address the open review thread and re-request review.
|
|
``
|
The chaos scenario
reconcile-sparkexposed a gap in create-pull-request bundling: replaying a diverged history onto the current GraphQL parent could fail during automatic rebase and drop into fallback issue flow. This change hardens replay behavior so reconciled divergence is handled predictably and tiny edits are preserved.Replay strategy for diverged history
reconcile-spark-style histories.Conflict-safe bundling path
Failure diagnostics
sanitizeContent+neutralizeClosingKeywordsForIssueBody) before embedding them in fallback issue bodies, preventing@mentionsand unsafe URLs from leaking into issue markdown.Git integration test for
reconcile-sparkscenariocaptures a push rejection error after applying a reconcile-spark diverged-history bundletocreate_pull_request_bundle_integration.test.cjs.@-mentions) thatsanitizeContentmust neutralize.pr-sous-chef: update branch requested by run https://github.com/github/gh-aw/actions/runs/27931286315